Gestion des consommations de café

Contexte

Considérons le scénario suivant :

Il s'agit d'écrire une application Java permettant de suivre la consommation de café des programmeurs d'un projet de développement logiciel. Chaque semaine, le chef de projet relève le nombre de tasses de café consommées par les différents programmeurs. Ces informations seront stockées dans deux tables d'une base de données relationnelle :

  • cafesdb.programmeurs qui stocke les informations d'identification de chaque programmeur du projet,
  • cafesdb.consos_cafe qui stocke les consommations de café semaine par semaine des différents programmeurs.

La structure de ces tables est la suivante :

Figure 1: Les tables pour la gestion des cafés.

1. Créer la base de données PostgreSQL

Vous allez dans un premier temps créer la base de données sur le serveur de bases de données PostgreSQL de l'IUGA (Institut d'Urbanisme et de Géographie Alpine de l'Université Grenoble Alpes) que nous utiliserons ultérieurement lors du projet d'intégration.

Pour accéder à ce serveur de bases de données vous aller utiliser le client pgAdmin qui est installé sur les PC de l'UFR IM2AG (vous pouvez aussi l'installer sur votre propre PC si vous le souhaitez, pour cela téléchargez l'application pgAdmin ici).

Etape 1: lancer pgAdmin et vosu connecter à serveur PostreSQL de l'IUGA.

  1. Lancer l'application pgAdmin. Sur les PC de l'UFR UFR IM2AG ouvrez un terminal et tapez la commande pgadmin4 &

  2. pgAdmin vous demande de fournir un mot de passe maître qui permettra ensuite de crypter les mots de passe que vous utiliserez pour vous connecter à différents serveurs de base de données. Fournissez-en un (choissisez en un dont vous êtes sûr de vous rappeler, et éventuellement notez le).

    pgAdmin : saisie du mot de passe maître
  3. Pour enregistrer le serveur PostgreSQL de l'IUGA

    1. faites un clic droit sur l'icone Servers dans le Browser de pgAdmin 4 et sélectionnez Register > Server

      pgAdmin : enregistrement du serveur PostgreSQL de l'IUGA
    2. dans la fenêtre pop-up Register - Server dans l'onglet General donnez un nom au serveur (par exemple PostgresIUGA)

      pgAdmin : donner un nom au serveur PostgreSQL de l'IUGA
    3. Dans l'onglet Connection définissez les paramètres de connexion au serveur

      • le numéro IP du serveur : 129.88.175.104
      • le port de connexion : 5432
      • votre identifant : votre identifiant agalan
      • votre mot de passe : votre mot de passe agalan
      pgAdmin : définir et enregistrer les paramètres de connection au serveur
    4. Enregistrez la configuration

Maintenant que vous avez enregistré le serveur PostgreSQL de l'IUGA vous allez créer la base de données et y insérer des données.

2: Créer la base de données

  1. Créez un répertoire TP12 dans votre répertoire de travail PLAI/Java

    Danc ce répertoire, téléchargez le fichier createCafesDB.sql qui contient un script peremtttant de créer un nouveau schéma de base de données de nom cafesdb, de créer les tables et d'insérer des données sur la consommations de cafés de l'équipe enseignante du M2CCI,

  2. Sélectionnez votre base de données parmi celles hébergées par le serveur de l'IUGA comme indiqué ci-dessous. Votre base de donnée à pour nom bd_XXXX est votre login agalan

    pgAdmin : sélectionner votre base de données sur le serveur PostgreSQL de l'IUGA
  3. Chargez le script createCafesDB.sql dans l'outil de requêtage Query Tool

    pgAdmin : charger le script createCafesDB.sql
  4. Exécutez le script createCafesDB.sql et vérifiez que le schéma cafesdb a bien été créé.

    pgAdmin : exécuter le script createCafesDB.sql
  5. Vérifiez le contenu de la table cafesdb.programmeurs

    pgAdmin : vérifier le contenu de la table cafesdb.programmeurs

    Vous devez obtenir le résultat suivant dans l'onglet Data Output

    pgAdmin : contenu de la table cafesdb.programmeurs
  6. Faites de même pour la table cafesdb.consos_cafe.

2. Ecriture d'un premier programme JDBC

Avant de vous interesser au programme de gestion des consommations de café proprement dit, vous allez écrire un premier programme permettant simplement de lire le contenu de la table cafesdb.programmeurs et de l'afficher sur la console.

Exercice 2.1

  1. Dans votre répertoire de travail (TP12), créez un projet maven d'application Java de groupId fr.im2ag.m2cci et de nom (artifactId) test-jdbc (pour avoir les différentes étapes à suivre vous pouvez vous référer aux explications fournies dans le TP Junit : Créer un projet Java avec Maven sous VSCode)

  2. Modifier la d'une méthode main de la classe Java Main afin qu'elle contienne le code suivant :

        public static void main(String[] args) {
            Console console = System.console();
            if (console == null) {
                System.out.println("Impossible d'obtenir une instance de console");
                System.exit(0);
            }
                                    
            String userName = console.readLine("Identifiant : ");
            char[] passwordArray = console.readPassword("Mot de passe : ");
                                             
            try (Connection conn = DriverManager.getConnection("jdbc:postgresql://129.88.175.104:5432/bd_" + userName,
                    userName, new String(passwordArray))) {
                System.out.println("Connexion réussie !");
                // à compléter plus tard
                // ...
            } catch (SQLException ex) {
                System.out.println("Erreur SQL " + ex.getMessage());
                ex.printStackTrace();
            }
        }

    les paramètres du message DriverManager.getConnection() sont dans l'ordre :

    • l'url de la base de données. Elle est de la forme

      jdbc:postgresql://serveur:port/nom_de_la_bd

      • serveur est le nom IP ou le numéro IP de la machine hébergeant le serveur de base de données, (ici le serveur PostgreSQL de l'IUGA a pour numéro IP : 129.88.175.104)
      • port est numéro de port pour les connexions au serveur PostgreSQL (ici 5432),
      • nom_de_la_bd est le nom de la bd, (ici bd_XX où XX est son login agalan).

    • votre identifiant pour la bd (votre identifiant agalan)

    • votre mot de passe pour la bd (votre mot de passe agalan)

  3. Exécutez ce programme et constatez qu'il provoque une erreur (SQLException)

    Erreur d'exécution de l'application Java

    D'après vous qu'elle est la cause de cette erreur et que faut-il faire pour la corriger ?

    Le pilote (driver) JDBC pour PostgreSQL n'a pas été trouvé, en effet il faut que le fichier .jar du pilote soit dans le classpath de l'application au moment de l'exécution. Ici, comme le projet est géré avec maven, il suffit de rajouter une dépendance vers un pilote jdbc pour Postgres. Maven se chargera de télécharger le fichier .jar et ensuite de l'inclure au classpath.

    Pour trouver cette dépendance, vous pouvez vous rendre sur maven repository https://mvnrepository.com et faire une recherche avec les termes PostgreSQL jdbc driver.

    ajout d'une dépendance maven au pilote PostgreSQL JDBC
  4. Corrigez la cause de l'erreur et vérifiez que le programme s'execute correctement.

    Exécution de l'application Java : la connexion à la base de données a pu être établie

Exercice 2.2 : complétez le programme pour qu'il affiche le contenu de la table cafesdb.programmeurs par ordre alphabétique des noms.

Affichage de la table cafesdb.programmeurs

Une fois l'objet connexion obtenu, créez à l'aide de celui ci un objet Statement et utilisez la méthode executeQuery de celui-ci pour effectuer la requête SQL SELECT.

Cette méthode retourne les résultats sous la forme de lignes de données encapsulées dans un objet ResultSet. Parcourez ce résultat ligne par ligne en utilisant les méthodes next() et getXXX() de l'interface ResultSet.

3. L'application GestionCafes

La base données ayant été installée, un premier programme simple vous ayant permis de comprendre le fonctionnement de base du JDBC, il est maintenant temps de passer à la réalisation d'une véritable application de gestion des consommations de cafés qui permettra :

  • d'effectuer des requêtes pour connaître :
    • le programmeur ayant consommé le plus de cafés sur une semaine,
    • le nombre total de tasses consommées par un programmeur donné,
    • les consommations de tous le programmeur pour une semaine donnée,
    • l'ensemble des consommations hebdomadaires d'un programmeur donné,
  • de saisir les consommations pour une semaine donnée,
  • d'effectuer une requête quelconque,
  • d'exporter des données vers un fichier texte (CVS)
  • d'importer des données depuis un fichier texte (CSV)

3.1 Récupérer le projet gestion-cafes

Question :

  1. Pour vous aider à réaliser ce TP, un squelette de l'application vous est fourni dans le projet gestion-cafes.zip. Téléchargez ce fichier et décompressez le dans votre répertoire de travail (Java/TP12).

  2. Ouvrez ce projet avec votre IDE (VSCode)et verifiez que le programme principal contenu dans la classe GestionCafes compile et s'exécute correctement. Pour le moment, aucune des opérations liés à la base de données (ouverture et la fermeture de la connexion, requêtes SQL, affichage des résultats) ne sont implémentées. Cela sera à vous de le faire dans les exercices suivants.

    Ce programme a la structure suivante:

    Ouvrir une connexion JDBC.
    répéter
        afficher un menu proposant différentes opérations à l'utilisateur
        effectuer l'opération choisie
    tant que l'utilisateur veut continuer  
    Fermer la connexion
  3. Parcourez le code du projet pour comprendre l'organisation de l'application.

3.2 : Rechercher de l'information dans la base de données

L'application GestionCafes permet à l'utilisateur de consulter les informations suivantes:

  • le ou les programmeurs ayant consommé le nombre maximum de cafés en une semaine et leur consommation totale pour cette semaine.

    exemple d'exécution de cette requête dans le programme GestionCafes (surlignées en jaune les réponse de l'utilisateur)

    ------------------------------------------
    1 : Plus gros consommateurs sur une semaine
    2 : Nombre total de tasses consommées par un programmeur
    3 : Consommations pour une semaine donnée
    0 : Quitter l'application
    votre choix : 1
    
    Le plus gros consommateur de cafés sur une semaine est :
    Catherine PARENT
    sa consommation est de 29 tasses de café la semaine 1

    Une requête SQL permettant de récupérer cette information est

    SELECT programmeur,prenom,nom,nb_tasses,no_semaine FROM cafesdb.consos_cafe c 
           JOIN  cafesdb.programmeurs p ON p.ID=c.programmeur WHERE
           c.nb_tasses=(SELECT MAX(nb_tasses) FROM cafesdb.consos_cafe)
  • pour un programmeur donné le nombre total de tasses de café consommées.

    exemple d'exécution de cette requête dans le programme GestionCafes

    ------------------------------------------
    1 : Plus gros consommateurs sur une semaine
    2 : Nombre total de tasses consommées par un programmeur
    3 : Consommations pour une semaine donnée
    0 : Quitter l'application
    votre choix : 2
    
    Nom du programmeur : SICARD
    Pascal SICARD a consommé un total de : 148

    Une requête SQL permettant de récupérer cette information est

    SELECT SUM(nb_tasses),nom,prenom  FROM cafesdb.consos_cafe c 
           JOIN  cafesdb.programmeurs p ON p.ID=c.programmeur 
           WHERE p.nom='SICARD' GROUP BY nom,prenom
  • pour une semaine donnée la liste des programmeurs triée dans l'ordre décroissant selon leur nombre de consommations.

    exemple d'exécution de cette requête dans le programme GestionCafes

    ------------------------------------------
    1 : Plus gros consommateurs sur une semaine
    2 : Nombre total de tasses consommées par un programmeur
    3 : Consommations pour une semaine donnée
    0 : Quitter l'application
    votre choix : 3
    
    Numéro de la semaine : 2
    Programmeur Catherine PARENT : 29
    Programmeur Virginia GARDNER : 22
    Programmeur Pascal SICARD : 19
    Programmeur Christian ENE : 18
    Programmeur Philippe GENOUD : 11
    Programmeur Sybille CAFFIAU : 6
    Programmeur Jean Marie FAVRE : 4
    Programmeur Mario CORTES-CORNAX : 3
    Programmeur Philippe WAILLE : 2
    Le nombre total de tasses consommé la semaine 2 est 114
    

    Une requête SQL permettant de récupérer cette information est

    SELECT programmeur,  prenom, nom, nb_tasses FROM 
           cafesdb.consos_cafe c JOIN cafesdb.programmeurs p ON c.programmeur=p.ID 
           WHERE c.no_semaine=2
           ORDER BY nb_tasses DESC

Question 1: Complétez le code de l'application pour réaliser les traitements précédents. Le code JDBC pour effectuer les requêtes doit être dans la classe CafeDAO. Le programme principal se charge uniquement d'ouvrir la connexion à la BD, d'invoquer les méthodes de CafeDAO et d'afficher leur résultats.

slides du cours consacrés à l'ouverture d'une connexion JDBC

  1. Pour le traitement correspondant au choix 1 (Plus gros consommateurs sur une semaine) vous pouvez créer un objet de type Statement puis utiliser sa méthode executeQuery(String sql) en passant en paramètre la chaîne de caractères correspondant à la requête SELECT. Cette méthode retourne des résultats sous la forme de lignes de données dans un objet ResultSet. Les résultats sont examinés ligne par ligne en utilisant les méthodes next() et getXXX() de l'interface ResultSet.

  2. Pour les traitements correspondants aux choix 2 et 3 (Nombre total de tasses consommées par un programmeur et Consommations pour une semaine donnée) vous créerez un objet de type PreparedStatement puis utiliserez ses méthodes setInt(int parameterIndex, int x) ou setString(int parameterIndex, String x) pour fixer la valeur des paramètres fournis par l'utilisateur. Vous pourrez ensuite executer la réquête avec la méthode executeQuery() qui retourne des résultats sous la forme de lignes de données dans un objet ResultSet. Les résultats sont examinés ligne par ligne en utilisant les méthode next() et getXXX() de l'interface ResultSet.

    slides du cours consacrés aux prepared Statements

Question 2: (facultatif, vous pouvez passer à la suite et le faire plus tard)

Ajoutez à l'application une fonctionnalité qui permet d'afficher toutes les consommations de café d'un programmeur donné triées par numéro de semaine croissant .

3.3 : Insérer des informations dans la base de données

On souhaite ajouter à l'application GestionCafes une fonctionnalité permettant à l'utilisateur de saisir et d'enregistrer dans la base de données les consommations des programmeurs pour une semaine donnée.

Le programme permet de saisir un numéro de semaine et ensuite pour chaque programmeur qui n'a pas déjà de consommation pour cette semaine de rentrer le nombre de tasses qu'il a consommées durant celle-ci.

exemple d'exécution de cette requête dans le programme GestionCafes

------------------------------------------
1 : Plus gros consommateurs sur une semaine
2 : Nombre total de tasses consommées par un programmeur
3 : Consommations pour une semaine donnée
4 : Saisie des consommations pour une semaine
0 : Quitter l'application
votre choix : 4

Numéro de la semaine : 9
nbre de tasses pour Jean Marie FAVRE (1) : 4
nbre de tasses pour Catherine PARENT (2) : 2
nbre de tasses pour Christian ENE (3) : 6
nbre de tasses pour Philippe WAILLE (4) : 0
nbre de tasses pour Philippe GENOUD (5) : 3
nbre de tasses pour Pascal SICARD (6) : 8
nbre de tasses pour Sybille CAFFIAU (7) : 7
nbre de tasses pour Mario CORTES-CORNAX (8) : 1
nbre de tasses pour Virginia GARDNER (9) : 4

Question : Modifiez l'application GestionCafes afin d'implémenter cette fonctionnalité d'insertion de données dans la base.

une requête SQL permettant pour une semaine donnée de trouver tous les programmeurs pour lesquels aucune consommation n'est enregistrée est

SELECT ID, prenom, nom FROM cafesdb.programmeurs 
          WHERE ID NOT IN (SELECT programmeur FROM cafesdb.consos_cafe WHERE no_semaine = numsem ) ORDER BY ID

numSem est le numéro de la semaine concernée.

une requête SQL permettant d'enregistrer la consommation d'un programmeur est

INSERT INTO cafesdb.consos_cafe (no_semaine, programmeur, nb_tasses)
       VALUES(numSem, idProg, nbTasses)

  • numSem est le numéro de la semaine concernée.
  • idProg est l'identifiant du programmeur.
  • nbTasses est le nombre de tasses qu'il a consommée durant cette semaine.

Pour exécuter les instructions SQL INSERT depuis le code Java, utilisez la méthode executeUpdate() d'un objet PreparedStatement qui retourne un entier (le nombre de lignes modifiées dans la table).


3.4 : Effectuer une requête libre et obtenir la méta information sur les types de données du résultat

L'application GestionCafes offre à l'utilisateur la possibilité d'exécuter n'importe quelle requête SQL qu'il peut entrer directement au clavier. Le résultat affiché dépendra de la nature de la requête.

  • Si la requête est type SELECT les informations suivantes sont affichées:
    • le nombre de colonnes de la table résultat est affiché,
    • pour chaque colonne, son nom et le type des données est affiché,
    • le contenu de la table est affiché ligne par ligne.

    Par exemple, si la requête est :
    SELECT * FROM cafesdb.programmeurs ORDER BY ID

    le résultat affiché sera :

    le résultat contient 4 colonnes
    --------------------------------
    Colonne : 1
    nom ID TYPE : INT
    --------------------------------
    Colonne : 2
    nom nom TYPE : VARCHAR
    --------------------------------
    Colonne : 3
    nom prenom TYPE : VARCHAR
    --------------------------------
    Colonne : 4
    nom BUREAU TYPE : INT
    --------------------------------
    Résultats de la requête
    
    1 FAVRE Jean Marie 412 
    2 PARENT Catherine 201 
    3 ENE Christian 208 
    4 WAILLE Philippe 401 
    ...
    alors que la requête :
    SELECT SUM(nb_tasses) FROM cafesdb.consos_cafe WHERE no_semaine = 1

    conduira à l'affichage suivant :

    le résultat contient 1 colonne
    --------------------------------
    Colonne : 1
    nom SUM(nb_tasses) TYPE : INTEGER
    --------------------------------
    Résultats de la requête
    
    84 
  • Sinon (la requête correspond à un UPDATE, INSERT, DELETE, DROP TABLE, CREATE TABLE, ..) le résultat affiché est alors le nombre de lignes modifiées dans la table concernée.

    Par exemple, si la requête est :
    INSERT INTO cafesdb.consos_cafe(no_semaine, programmeur, nb_tasses) VALUES (41,2,10)
    le résultat affiché sera :
    Mise à jour de la base effectuée
    Nombre de lignes modifiées : 1

Question: Modifiez le code de la méthode requeteLibreEtMetaDonnees() de la classe GestionCafes afin d'implémenter les opérations permettant de supporter ces requêtes libres.

Pour exécuter une requête SQL libre depuis le code Java, vous utiliser la méthode execute() d'un objet Statement qui retourne un booléen de valeur true si la requête est de type SELECT, de valeur false sinon.

Si la requête a produit un ResultSet, vous devrez utiliser les méta-données de celui-ci (classse ResultSetMetaData) pour obtenir les descriptions des colonnes.

slides du cours consacrés aux méta données et aux requêtes libres

3.5: Import/Export des données (Batch Updates)

Avant de disposer de l'application GestionCafes le manager utilisait un tableur pour saisir les consommations de cafés. Afin de lui permettre de continuer à utiliser éventuellement son outil préféré, on souhaite doter l'application GestionCafes d'une fonctionnalité d'import/export depuis/vers des fichiers textes au format CSV (Comma Separated Values, fichiers textes où les valeurs sont séparées par des virgules).

L'import/export se fait sur la base des tables de la base de données : à une table correspond un fichier. La figure ci dessous montre l'exemple de l'export des données des tables cafesdb.programmeurs et cafesdb.consos_cafe

export des tables cafesdb.programmeurs et cafesdb.consos_cafe

Le format des fichiers CSV est le suivant :

  • La première ligne contient les descriptions des colonnes de la table sous la forme NomDeLaColonne:TypeSQLDeLaColonne séparées par des virgules. Par exemple pour la table cafesdb.programmeurs on aura :
    ID:INT,nom:VARCHAR,prenom:VARCHAR,BUREAU:INT
  • Les lignes suivantes contiennent les valeurs des enregistrements présents dans la table, valeurs séparées par des virgules. Par exemple un enregistrement dans la table cafesdb.programmeurs sera écrit sous la forme :
    1,FAVRE,Jean Marie,412

Question 1: Modifiez la classe TableReaderWriter et le programme principal GestionCafes pour implémenter la fonctionnalité permettant d'exporter les données d'une table vers un fichier texte au format CSV.

La requête pour récupérer les données d'une table ne pose pas de problème (la sortie étant un fichier texte, on peut appliquer la méthode getString() sur chacune des colonnes du ResultSet). La seule 'difficulté' consiste à récupérer les noms et les types des colonnes pour produire la première ligne du fichier. Pour cela vous disposez d'un objet ResultSetMetaData que vous pouvez obtenir via le ResultSet.

Question 2: De manière symétrique, modifiez la classe TableReaderWriter et le programme principal GestionCafes pour implémenter la fonctionnalité d'import des données à partir des fichiers texte au format CSV

L'opération d'import consiste à effacer le contenu d'une table cible, et de le remplacer par les données d'un fichier .csv. Le fichier .csv est ouvert en lecture et pour chaque ligne lue une opération d'insertion correspondante est réalisée dans la base de données.

Pour effectuer les test d'import deux fichiers de données ont été créés

Le nombre de données étant relativement important (1612 lignes pour consos.csv), pour des raisons d'efficacité il vous est recommandé d'utiliser des Batch Updates plutôt que de faire les insertions avec de simple objets Statement et des messages executeUpdate() (pour en savoir plus, voir le cours JDBC transparents 41 et 42).

slides du cours consacrés aux BatchUpdates